package com.aaa.pdfdemo;

import android.content.Context;
import android.util.Log;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;

/**
 * create by water.yuan
 * on 2021-04-05
 * <p>
 * 复制Assets文件到外部存储
 * 解压带中文文件
 */
public class FileUtils {
    private static final String TAG = FileUtils.class.getSimpleName();

    /**
     * 获取应用外部专属file根文件
     * 比如/storage/emulated/0/Android/data/包名/files
     */
    public static File getExternalFileRoot(Context ctx) {
        Context context = ctx.getApplicationContext();
        File externalFilesDir = context.getExternalFilesDir(null);
        Log.d(TAG, "externalFilesDir=" + externalFilesDir);
        return externalFilesDir;
    }

    /**
     * 获取应用外部专属file根文件路径
     * 比如/storage/emulated/0/Android/data/包名/files
     */
    public static String getExternalFileRootPath(Context ctx) {
        String absolutePath = "";
        File externalFileRoot = getExternalFileRoot(ctx);
        if (externalFileRoot != null) {
            absolutePath = externalFileRoot.getAbsolutePath();
            Log.d(TAG, "getExternalFileRootPath: " + absolutePath);
        }
        return absolutePath;
    }

    /**
     * @param ctx            上下文
     * @param assetsFilePath assets目录下的文件路径，包含文件后缀，比如xyx-guild.zip
     * @param desFilePath    复制到目的文件的绝对路径，比如/storage/emulated/0/Android/data/包名/files/xyx-guild.zip
     * @Description 复制Assets文件到外部存储（耗时操作）
     */
    public static boolean cpAssetsFile2(Context ctx, String assetsFilePath, String desFilePath) {
        boolean isSuccess = false;
        Context context = ctx.getApplicationContext();
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = context.getAssets().open(assetsFilePath);
            File file = new File(desFilePath);
            if (file != null) {
                if (file.exists()) {
                    Log.w(TAG, "cpAssetsFile2: desFile exist");
                } else {
                    outputStream = new FileOutputStream(file);
                    byte[] buffer = new byte[1024];
                    int byteRead;
                    while ((byteRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, byteRead);
                    }
                    outputStream.flush();
                    isSuccess = true;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "cpAssetsFile2: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                    outputStream = null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (inputStream != null) {
                    inputStream.close();
                    inputStream = null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return isSuccess;
        }
    }

    /**
     * 解压文件时的回调接口
     */
    public interface ZipCallback {
        default void onStart() {
        }

        void onSuccess();

        void onError(String msg);
    }

    /**
     * 解压ZIP文件到指定路径。已适配带中文路径，适用于Android任何版本
     * <p>
     * 注意：Java自带的文件解压类@{@link java.util.zip.ZipFile}在使用上具有局限性，
     * Android 7.0 api24之后的jdk1.7能解决中文，但不能适配Android 6.0及之前
     * <p>
     * 所以这里采用apache官网提供的ant.jar来实现文件解压处理，ant.jar下载自apache官网，理论上来讲不存在安全风险问题。
     * <p>
     * 因为解压文件属于耗时操作，尤其是在文件比较大的情况下，所以这里使用子线程进行处理，避免影响UI性能。
     *
     * @param srcFile     ZIP文件，比如/storage/emulated/0/Android/data/包名/files/xyx-guild.zip
     * @param desDirPath  解压到的路径，比如/storage/emulated/0/Android/data/包名/files
     * @param zipCallback 用于和UI线程交互
     */
    public static void unZipFile(File srcFile, String desDirPath, ZipCallback zipCallback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                zipCallback.onStart();
                FileOutputStream fileOutputStream = null;
                InputStream inputStream = null;
                byte[] buffer = new byte[1024];
                int len = 0;
                try {
                    ZipFile zipFile = new ZipFile(srcFile, "gbk"); // 整个压缩文件
                    ZipEntry entry = null; // 压缩文件中的每个实体
                    File file; // 每个实体对应的文件，可能是文件夹或文件
                    Enumeration entries = zipFile.getEntries();
                    while (entries.hasMoreElements()) {
                        entry = (ZipEntry) entries.nextElement();
                        Log.d(TAG, "entry.getName: " + entry.getName());
                        file = new File(desDirPath + File.separator + entry.getName());
                        // 是文件夹则创建含父路径的文件夹
                        if (entry.isDirectory()) {
                            file.mkdirs();
                        } else {
                            // 是文件，则先创建父路径等文件夹，避免自身被创建成文件夹
                            File parent = file.getParentFile();
                            if (!parent.exists()) {
                                parent.mkdirs();
                            }
                            inputStream = zipFile.getInputStream(entry); // 从整个压缩文件中获取每个实体的出入流
                            Log.d(TAG, "unZipFile: " + file.getAbsolutePath());
                            fileOutputStream = new FileOutputStream(file);
                            while ((len = inputStream.read(buffer)) > 0) {
                                fileOutputStream.write(buffer, 0, len);
                            }
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "unZipFile: " + e.getMessage());
                    zipCallback.onError(e.getMessage());
                    e.printStackTrace();
                } finally {
                    try {
                        if (fileOutputStream != null) {
                            fileOutputStream.close();
                        }
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        zipCallback.onSuccess();
                    } catch (Exception e) {
                        Log.e(TAG, "unZipFile finally: " + e.getMessage());
                        zipCallback.onError(e.getMessage());
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

